﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CLFramework.Windows.Forms;
using System.Xml;
using StockMonitor.Helper;
using CLFramework.Utility;
using System.Threading;
using StockMonitor.Business;
using StockMonitor.Configuration;
using System.IO;
using StockMonitor.Forms;
using CLFramework.Utility;

namespace StockMonitor.UserControls
{
    [ModuleContract("3C89BC3D-E108-4f5d-91F9-9832CFF08FE7", "ETF(&E)", "ETF交易")]
    public partial class UCETFTrade : MdiUserControl
    {
        HsGateway hs = new HsGateway();

        #region Ctor.

        public UCETFTrade()
            : base()
        {
            InitializeComponent();

            Log.OnLogged += new EventHandler<LogEventArgs>(Log_OnLogged);
        }

        public UCETFTrade(XmlNode node)
            : this()
        {
            InitializeComponent();
        }

        #endregion

        #region Members

        /// <summary>
        /// 自动交易是否启动（与GlobalStatusContext.ETFTradeStarted同步）
        /// </summary>
        /// <remarks>
        /// 代码编写：<author>LANCEZHANG-PC\zhangxl</author><br/>
        /// 编写时间：<datetime>2012-10-16 5:33 PM</datetime>
        /// </remarks>
        bool tradeStarted = false;
        bool TradeStarted
        {
            get { return tradeStarted; }
            set
            {
                tradeStarted = value;
                GlobalStatusContext.ETFTradeStarted = tradeStarted;
            }
        }

        /// <summary>
        /// 自动交易线程
        /// </summary>
        /// <remarks>
        /// 代码编写：<author>LANCEZHANG-PC\zhangxl</author><br/>
        /// 编写时间：<datetime>2012-10-16 5:33 PM</datetime>
        /// </remarks>
        Thread threadAutoTrade;

        #endregion

        #region EventHandler

        /// <summary>
        /// Handles the Tick event of the timerUpdateUI control.
        /// 1. 刷新余额
        /// 2. 刷新累计成交
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        /// <remarks>
        /// 代码编写：<author>LANCEZHANG-PC\zhangxl</author><br/>
        /// 编写时间：<datetime>2012-10-16 5:38 PM</datetime>
        /// </remarks>
        private void timerUpdateUI_Tick(object sender, EventArgs e)
        {
            ThreadPool.QueueUserWorkItem(o =>
            {
                while (!this.IsHandleCreated)
                    Thread.Sleep(100);

                this.Invoke(new Action(() =>
                {
                    this.dgvAccountInfo["CurrentBalance", 0].Value = AuthUserContext.Instance.CurrentBalance;
                    this.dgvAccountInfo["EnableBalance", 0].Value = AuthUserContext.Instance.EnableBalance;
                    this.dgvAccountInfo["FetchCash", 0].Value = AuthUserContext.Instance.FetchCash;
                    this.dgvAccountInfo["AssetBalance", 0].Value = AuthUserContext.Instance.AssetBalance;

                    this.dgvAccountInfo.Update();
                    this.dgvAccountInfo.Refresh();

                    //todo: 持仓情况是否要在此更新？或是pool里自动更新了？
                    //for (int i = 0; i < this.dgvPool.RowCount; i++)
                    //    this.dgvPool["col1TodayExchangeSummary", i].Value = StockPool.Instance.SellPool[i].TodayExchangeSummary;

                    this.dgvPool.Refresh();
                    this.dgvPool.Update();
                    this.dgvPool.Refresh();
                }));

            });

            this.numETFMADownRate.Enabled = !this.TradeStarted;
            this.numETFMAUpRate.Enabled = !this.TradeStarted;
            this.btnStartAutoTrade.Enabled = !this.TradeStarted;
            this.btnEditPool.Enabled = !this.TradeStarted;
            //this.btnEditBuy.Enabled = !this.TradeStarted;
            this.btnStopAutoTrade.Enabled = this.TradeStarted;

            //LoadModelsInfo();
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("您确定要退出程序吗？", "退出确认", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.OK)
            {
                Log.Info("退出系统");
                TradeStarted = false;
                Application.Exit();
            }
        }

        private void btnStartAutoTrade_Click(object sender, EventArgs e)
        {
            StringBuilder sb = new StringBuilder("请确认以下配置信息\n\n");

            AppSettings.SetValue("ETFMAMins", Convert.ToInt32(this.numETFMAMins.Value));

            sb.AppendLine(string.Format("【使用均线为{0}分钟{1}MA】", AppSettings.GetValue<double>("ETFMAMins", Convert.ToInt32(this.numETFMAMins.Value), true), AppSettings.GetValue<double>("ETFMAIndex", Convert.ToInt32(this.numETFMAIndex.Value), true)));
            sb.AppendLine("入场：");
            sb.AppendLine("价格从下上穿21MA{REF(CLOSE,1)≤REF(21MA,1)&CLOSE>21MA）}（21已定）&收阳线（CLOSE>OPEN），做多。");
            sb.AppendLine("离场：");
            sb.AppendLine(string.Format("1、价格上涨幅度大于{0}‰，最大的收盘价涨幅回落{1}%以上（含），离场。", AppSettings.GetValue<double>("ETFMAUpRate", Convert.ToDouble(this.numETFMAUpRate.Value) * 0.001, true) * 1000, AppSettings.GetValue<double>("ETFMADownRate", Convert.ToDouble(this.numETFMADownRate.Value) * 0.01, true) * 100));
            sb.AppendLine("2、收盘价格下穿21MA（CLOSE<21MA），离场。");
            sb.AppendLine("3、收盘之前14:59离场。");

            sb.AppendLine("\n监控ETF如下：");
            ETFStockPool.Instance.Pool.ForEach(s => sb.AppendLine(string.Format(" {0}({1} 交易数量：{2})", s.StockCode, s.StockName, s.ExchangeAmount)));

            if (MessageBox.Show(sb.ToString(), "ETF自动交易配置确认", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
                return;

            Log.Info("ETF自动交易已开始");
            this.TradeStarted = true;

            threadAutoTrade = new Thread(new ThreadStart(AutoTrade));
            threadAutoTrade.IsBackground = true;
            threadAutoTrade.Priority = ThreadPriority.Highest;
            threadAutoTrade.Start();
        }

        private void btnStopAutoTrade_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("确定要停止自动交易吗？", "确认", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.OK)
            {
                this.TradeStarted = false;
                Log.Info("自动交易已停止");
            }
        }

        /// <summary>
        /// Handles the Click event of the btnRefresh control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        /// <remarks>
        /// 代码编写：<author>LANCEZHANG-PC\zhangxl</author><br/>
        /// 编写时间：<datetime>2012-10-16 5:37 PM</datetime>
        /// </remarks>
        private void btnRefresh_Click(object sender, EventArgs e)
        {
            Onload();
        }

        private void btnSaveConfig_Click(object sender, EventArgs e)
        {

        }

        #endregion

        #region MdiUserControl Members

        /// <summary>
        /// 窗体加载.
        /// 刷新今日成交记录。
        /// </summary>
        /// <remarks>
        /// 代码编写：<author>LANCEZHANG-PC\zhangxl</author><br/>
        /// 编写时间：<datetime>2012-10-16 5:40 PM</datetime>
        /// </remarks>
        public override void Onload()
        {
            this.lbUserAccount.Text = AuthUserContext.Instance.FundAccount;
            this.lbUserName.Text = AuthUserContext.Instance.ClientName;

            AuthUserContext[] info = { AuthUserContext.Instance };
            this.dgvAccountInfo.AutoGenerateColumns = false;
            this.dgvAccountInfo.DataSource = info;

            this.dgvPool.AutoGenerateColumns = false;
            LoadStockPool();

            //Load Model's Init infor.
            //LoadModelsInfo();

            //create connection.
            hs.Connect();

            try
            {
                TradeContext.Instance.UpdateTodayBusiness();
                RefreshStockPositionUI();
            }
            catch (HsGateway.HsCommException hsex)
            {
                Log.Exception("交易接口返回错误：" + ((int)hsex.ErrorInfo).ToString(), hsex);
            }
            catch (Exception ex)
            {
                Log.Exception("捕获到系统错误A02", ex);
            }
        }

        /// <summary>
        /// 窗体卸载.
        /// </summary>
        /// <remarks>
        /// 代码编写：<author>LANCEZHANG-PC\zhangxl</author><br/>
        /// 编写时间：<datetime>2012-10-16 5:40 PM</datetime>
        /// </remarks>
        public override void Unload()
        {
            base.Unload();
            hs.Dispose();
        }

        #endregion

        #region Methods.

        /// <summary>
        /// 根据股票池更新股票池显示的UI，用于弹出窗回调
        /// </summary>
        /// <param name="item">股票池元素.</param>
        /// <remarks>
        /// 代码编写：<author>LANCEZHANG-PC\zhangxl</author><br/>
        /// 编写时间：<datetime>2012-10-16 5:51 PM</datetime>
        /// </remarks>
        public void ReLoadPoolItemUI(ETFPoolItem item)
        {
            this.dgvPool.DataSource = ETFStockPool.Instance.Pool;
        }

        /// <summary>
        /// 根据股票池更新股票池显示的UI.
        /// </summary>
        /// <remarks>
        /// 代码编写：<author>LANCEZHANG-PC\zhangxl</author><br/>
        /// 编写时间：<datetime>2012-10-16 5:53 PM</datetime>
        /// </remarks>
        public void LoadStockPool()
        {
            this.dgvPool.DataSource = ETFStockPool.Instance.Pool;
        }

        /// <summary>
        /// 刷新股票持仓情况UI，重新绑定GridView.
        /// </summary>
        /// <remarks>
        /// 代码编写：<author>LANCEZHANG-PC\zhangxl</author><br/>
        /// 编写时间：<datetime>2012-10-16 5:26 PM</datetime>
        /// </remarks>
        public void RefreshStockPositionUI()
        {
            if (StockPool.Instance.RefreshStockPosition())
            {
                if (this.dgvPool.InvokeRequired)
                {
                    this.Invoke(new Action(() =>
                    {
                        this.dgvPool.DataSource = ETFStockPool.Instance.Pool;
                        this.dgvPool.Update();
                        this.dgvPool.Refresh();
                    }));
                }
                else
                {

                    this.dgvPool.DataSource = ETFStockPool.Instance.Pool;
                    this.dgvPool.Update();
                    this.dgvPool.Refresh();
                }
            }
        }

        /// <summary>
        /// 执行交易操作.
        /// 若交易成功，3秒钟后刷新并显示结果
        /// </summary>
        /// <param name="stock">股票.</param>
        /// <param name="action">交易信号.</param>
        /// <returns></returns>
        /// <remarks>
        /// 代码编写：<author>LANCEZHANG-PC\zhangxl</author><br/>
        /// 编写时间：<datetime>2012-10-16 5:27 PM</datetime>
        /// </remarks>
        private int ExecuteTrade(ETFPoolItem stock, Enums.Signal action)
        {
            try
            {
                int result = 0;

                if (action > Enums.Signal.None) //买
                {
                    result = TradeContext.Instance.BuyOrder(stock.StockCode, stock.ExchangeAmount, Enums.PriceType.卖一);
                }
                else //卖
                {
                    int amount = stock.ExchangeAmount;
                    if (amount > stock.CurrentAmount)
                    {
                        Log.Info(stock.StockCode + "当前可卖数量不足，不能进行自动卖出");
                        return -2;
                    }
                    result = TradeContext.Instance.SellOrder(stock.StockCode, amount, Enums.PriceType.买一);
                }

                if (result > 0)
                {
                    ThreadPool.QueueUserWorkItem(o =>
                    {
                        Thread.Sleep(3000);
                        RefreshStockPositionUI();
                        AuthUserContext.Instance.RefreshBalance();
                        TradeContext.Instance.UpdateTodayEntrusts();

                        Log.Info(TradeContext.Instance.TodayEntrusts.Find(t => t.EntrustNo == result).ShowResult());

                        //note: 根据实际执行结果更新计算器中的参数，但3秒钟太晚了，其实需要少于检测时间，或者在计算器中标识正在交易状态，从而跳过检测
                    });
                    return result;
                }
                else
                {
                    Log.Info("委托失败，错误代码：" + result.ToString() + result.ToString() == "-2" ? " 可卖数量不足，不能进行自动卖出" : " ");
                    return result;
                }
            }
            catch (HsGateway.HsCommException hsex)
            {
                Log.Exception("交易接口返回错误A03", hsex);
                return Convert.ToInt32(hsex.ErrorInfo);
            }
            catch (Exception ex)
            {
                Log.Exception("捕获到系统错误A03", ex);
                return -1;
            }
        }

        /// <summary>
        /// 【线程】自动交易.
        /// 1.初始化，为股票池中的每只股票创建MA计算器
        /// 2.批量获取行情，并提供给每个计算器进行计算（信号检测）
        /// 3.将本次得到的信号逐个进行处理（交易）
        /// 4.如果股票池发生变动，保存股票池
        /// 5.休息，等待下次轮循
        /// </summary>
        /// <remarks>
        /// 代码编写：<author>LANCEZHANG-PC\zhangxl</author><br/>
        /// 编写时间：<datetime>2012-10-16 5:33 PM</datetime>
        /// </remarks>
        private void AutoTrade()
        {
            try
            {
                //只选取有买卖数量配置的股票
                List<string> codes = ETFStockPool.Instance.Pool.Where(s => s.ExchangeAmount > 0).Select(s => s.StockCode).Distinct().ToList();

                List<ETFMACalculator> cals = new List<ETFMACalculator>();

                Log.Info(string.Format("初始化ETFMA自动交易程序，参数为：{0}‰，{1}%", this.numETFMAUpRate.Value, this.numETFMADownRate.Value));

                codes.ForEach(c =>
                {
                    var cal = BusinessFactory.GetETFMACalculator();
                    if (!cal.Init(c))
                        Log.Warning(string.Format("【{0}】{1}分钟实时行情数尚且不够，无法侦测自动交易", c, this.numETFMAMins.Value));
                    else
                    {
                        Log.Warning(string.Format("{0} 自动交易侦测器初始化成功", c));
                        cals.Add(cal);
                    }
                });

                while (this.TradeStarted)
                {
                    bool needSavePool = false;
                    Dictionary<string, Enums.Signal> sigDic = new Dictionary<string, Enums.Signal>();

                    #region 获取当前价

                    Dictionary<string, StockPrice> currentPrices;
                    try
                    {
                        currentPrices = BusinessFactory.GetCurrentPriceProvider().BatchGetCurrentPrice(codes).ToDictionary(c => c.StockCode);
                    }
                    catch (Exception e)
                    {
                        Log.Exception("行情价格获取失败，请检查系统配置", e);
                        Thread.Sleep(AppSettings.GetValue<int>("SignalDetectInterval", 1000, true));
                        continue;
                    }

                    #endregion

                    #region 分别计算信号

                    cals.ForEach(cal =>
                    {
                        try
                        {
                            if (currentPrices.ContainsKey(cal.StockCode))
                            {
                                var signal = cal.DetectSignal(currentPrices[cal.StockCode].Price);

                                if (signal != Enums.Signal.None)
                                    sigDic.Add(cal.StockCode, signal);
                            }
                        }
                        catch (Exception e)
                        {
                            Log.Exception(string.Format("检测{0}({1})指标时发生错误", cal.Key, cal.StockCode), e);
                        }
                    });

                    #endregion

                    #region 挨个处理信号

                    foreach (var item in sigDic)
                    {
                        var stock = ETFStockPool.Instance.Pool.Find(s => s.StockCode == item.Key);

                        if ((stock != null) && CheckTime())
                        {
                            Log.Info(string.Format("{0}({2})检测到发出的{1}信号...", stock.StockCode, item.Value, stock.StockName));

                            if ((item.Value == Enums.Signal.Buy || item.Value == Enums.Signal.Sell) && (stock.ExchangeAmount > 0))
                            {
                                //Save last trigger.
                                needSavePool = true;

                                Log.Info(string.Format("{0}({2})进行自动{1}...", stock.StockCode, item.Value, stock.StockName));
                                ExecuteTrade(stock, item.Value);
                            }
                        }
                    }

                    #endregion

                    if (needSavePool)
                        ETFStockPool.Instance.Save();

                    Thread.Sleep(AppSettings.GetValue<int>("SignalDetectInterval", 1000, true));
                }
            }
            catch (Exception ex)
            {
                Log.Exception("捕获到网络环境异常A01", ex);
            }
        }

        /// <summary>
        /// 检查是否交易时间
        /// </summary>
        /// <returns></returns>
        private bool CheckTime()
        {
            DateTime start = DateTime.Today.AddHours(9).AddMinutes(30);
            return (DateTime.Now > start);
        }

        #endregion

        #region Log

        private Color GetLogColor(LogLevel level)
        {
            switch (level)
            {
                case LogLevel.Debug:
                    return Color.Cyan;
                case LogLevel.Error:
                    return Color.Red;
                case LogLevel.Fatal:
                    return Color.DarkRed;
                case LogLevel.Info:
                    return Color.Green;
                case LogLevel.Warn:
                    return Color.Yellow;
                default:
                    return Color.Green;
            }
        }

        private void LogText(string text)
        {
            if (rtbOutputInfo.InvokeRequired)
            {
                this.Invoke(new Action<string, Color>(LogText), text, rtbOutputInfo.ForeColor);
            }
            else
                LogText(text, rtbOutputInfo.ForeColor);
        }

        private void LogText(string text, Color color)
        {

            if (rtbOutputInfo.InvokeRequired)
            {
                this.Invoke(new Action<string, Color>((t, c) =>
                {
                    try
                    {
                        rtbOutputInfo.AppendText(text);
                        rtbOutputInfo.Select(rtbOutputInfo.TextLength - text.Length, text.Length);
                        rtbOutputInfo.SelectionColor = color;
                        rtbOutputInfo.AppendText(Environment.NewLine);

                        rtbOutputInfo.Select(rtbOutputInfo.Text.Length, rtbOutputInfo.Text.Length);
                        rtbOutputInfo.ScrollToCaret();
                    }
                    catch
                    {
                    }
                }), text, color);
            }
            else
            {
                try
                {
                    rtbOutputInfo.AppendText(text);
                    rtbOutputInfo.Select(rtbOutputInfo.TextLength - text.Length, text.Length);
                    rtbOutputInfo.SelectionColor = color;
                    rtbOutputInfo.AppendText(Environment.NewLine);

                    rtbOutputInfo.Select(rtbOutputInfo.Text.Length, rtbOutputInfo.Text.Length);
                    rtbOutputInfo.ScrollToCaret();
                }
                catch
                {
                }
            }
        }

        void Log_OnLogged(object sender, LogEventArgs e)
        {
            this.LogText(string.Format("【{0}】{1}", DateTime.Now.ToString("hh:mm:ss"), e.Message), GetLogColor(e.Level));
#if DEBUG
            if (e.Exception != null)
            {
                this.LogText(string.Format("错误信息：{0}", e.Exception.Message), GetLogColor(e.Level));
            }
#endif
        }

        #endregion

        private void btnEditPool_Click(object sender, EventArgs e)
        {
            if (this.dgvPool.SelectedRows.Count == 0)
                return;
            string stockCode = this.dgvPool.SelectedRows[0].Cells[0].Value.ToString();

            frmEditETFInPool dlg = new frmEditETFInPool(stockCode, ReLoadPoolItemUI);
            dlg.ShowDialog();
        }

        #region Test

        #endregion
    }
}
